%%% -------------------------------------------------------------------
%%% Author  : vivimice
%%% Description :
%%%
%%% Created : Feb 20, 2012
%%% -------------------------------------------------------------------
-module(tio_states_holder).

-behaviour(gen_server).
%% --------------------------------------------------------------------
%% Include files
%% --------------------------------------------------------------------

%% --------------------------------------------------------------------
%% External exports
%% --------------------------------------------------------------------
-export([start_link/1, stop/1]).
-export([get_foreground/1, set_foreground/2]).
-export([get_background/1, set_background/2]).
-export([get_bold/1, set_bold/2]).
-export([get_dim/1, set_dim/2]).
-export([get_underline/1, set_underline/2]).
-export([get_blink/1, set_blink/2]).
-export([get_inverse/1, set_inverse/2]).
-export([get_hidden/1, set_hidden/2]).
-export([clear_character_attributes/1]).
-export([get_cursor_pos/1, set_cursor_pos/2, move_cursor_pos/2, new_line/1]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).

-record(state, {context, foreground, background, bold, dim, underline, blink, inverse, hidden, cursor_pos}).

%% ====================================================================
%% External functions
%% ====================================================================
start_link(Context) ->
	{ok, Pid} = gen_server:start_link(?MODULE, [Context], []),
	Pid.

stop(Holder) ->
	gen_server:cast(Holder, stop).

%% ====================================================================
%% Server functions
%% ====================================================================
get_foreground(Holder) ->
	{foreground, Value} = gen_server:call(Holder, get_foreground),
	Value.

set_foreground(Holder, Value) ->
	ok = gen_server:call(Holder, {set_foreground, Value}).

get_background(Holder) ->
	{background, Value} = gen_server:call(Holder, get_background),
	Value.

set_background(Holder, Value) ->
	ok = gen_server:call(Holder, {set_background, Value}).

get_bold(Holder) ->
	{bold, Value} = gen_server:call(Holder, get_bold),
	Value.

set_bold(Holder, Value) ->
	ok = gen_server:call(Holder, {set_bold, Value}).

get_dim(Holder) ->
	{dim, Value} = gen_server:call(Holder, get_dim),
	Value.

set_dim(Holder, Value) ->
	ok = gen_server:call(Holder, {set_dim, Value}).

get_underline(Holder) ->
	{underline, Value} = gen_server:call(Holder, get_underline),
	Value.

set_underline(Holder, Value) ->
	ok = gen_server:call(Holder, {set_underline, Value}).

get_blink(Holder) ->
	{blink, Value} = gen_server:call(Holder, get_blink),
	Value.

set_blink(Holder, Value) ->
	ok = gen_server:call(Holder, {set_blink, Value}).

get_inverse(Holder) ->
	{inverse, Value} = gen_server:call(Holder, get_inverse),
	Value.

set_inverse(Holder, Value) ->
	ok = gen_server:call(Holder, {set_inverse, Value}).

get_hidden(Holder) ->
	{hidden, Value} = gen_server:call(Holder, get_hidden),
	Value.

set_hidden(Holder, Value) ->
	ok = gen_server:call(Holder, {set_hidden, Value}).

clear_character_attributes(Holder) ->
	ok = gen_server:call(Holder, clear_character_attributes).

get_cursor_pos(Holder) ->
	{cursor_pos, {Row, Column}} = gen_server:call(Holder, get_cursor_pos),
	{Row, Column}.

set_cursor_pos(Holder, {Row, Column}) ->
	ok = gen_server:call(Holder, {set_cursor_pos, {Row, Column}}).

move_cursor_pos(Holder, {RowOffset, ColumnOffset}) ->
	ok = gen_server:call(Holder, {move_cursor, {RowOffset, ColumnOffset}}).

new_line(Holder) ->
	ok = gen_server:call(Holder, new_line).

%% --------------------------------------------------------------------
%% Function: init/1
%% Description: Initiates the server
%% Returns: {ok, State}          |
%%          {ok, State, Timeout} |
%%          ignore               |
%%          {stop, Reason}
%% --------------------------------------------------------------------
init([Context]) ->
    {ok, #state{context = Context, foreground = 0, background = 0, bold = false, 
				dim = false, underline = false, blink = false,
				inverse = false, hidden = false, cursor_pos = {1, 1}}}.

%% --------------------------------------------------------------------
%% Function: handle_call/3
%% Description: Handling call messages
%% Returns: {reply, Reply, State}          |
%%          {reply, Reply, State, Timeout} |
%%          {noreply, State}               |
%%          {noreply, State, Timeout}      |
%%          {stop, Reason, Reply, State}   | (terminate/2 is called)
%%          {stop, Reason, State}            (terminate/2 is called)
%% --------------------------------------------------------------------
handle_call(get_foreground, _From, State) ->
	{reply, {foreground, State#state.foreground}, State};

handle_call({set_foreground, Value}, _From, State) ->
	{reply, ok, State#state{foreground = Value}};

handle_call(get_background, _From, State) ->
	{reply, {background, State#state.background}, State};

handle_call({set_background, Value}, _From, State) ->
	{reply, ok, State#state{background = Value}};

handle_call(get_bold, _From, State) ->
	{reply, {bold, State#state.bold}, State};

handle_call({set_bold, Value}, _From, State) ->
	{reply, ok, State#state{bold = Value}};

handle_call(get_dim, _From, State) ->
	{reply, {dim, State#state.dim}, State};

handle_call({set_dim, Value}, _From, State) ->
	{reply, ok, State#state{dim = Value}};

handle_call(get_underline, _From, State) ->
	{reply, {underline, State#state.underline}, State};

handle_call({set_underline, Value}, _From, State) ->
	{reply, ok, State#state{underline = Value}};

handle_call(get_blink, _From, State) ->
	{reply, {blink, State#state.blink}, State};

handle_call({set_blink, Value}, _From, State) ->
	{reply, ok, State#state{blink = Value}};

handle_call(get_inverse, _From, State) ->
	{reply, {inverse, State#state.inverse}, State};

handle_call({set_inverse, Value}, _From, State) ->
	{reply, ok, State#state{inverse = Value}};

handle_call(get_hidden, _From, State) ->
	{reply, {hidden, State#state.hidden}, State};

handle_call({set_hidden, Value}, _From, State) ->
	{reply, ok, State#state{hidden = Value}};

handle_call(clear_character_attributes, _From, State) ->
	{reply, 
	 ok, 
	 State#state{foreground = 0,
				 background = 0,
				 bold = false,
				 dim = false,
				 underline = false,
				 blink = false,
				 inverse = false,
				 hidden = false
				 }};

handle_call(get_cursor_pos, _From, State) ->
	{Row, Column} = State#state.cursor_pos,
	{reply, {cursor_pos, {Row, Column}}, State};

handle_call({set_cursor_pos, {Row, Column}}, _From, State) ->
	{MaxRows, MaxColumns} = context:get_window_size(State#state.context),
	ActualRow = utils:max(1, utils:min(MaxRows, Row)),
	ActualColumn = utils:max(1, utils:min(MaxColumns, Column)),
	{reply, ok, State#state{cursor_pos = {ActualRow, ActualColumn}}};

handle_call({move_cursor_pos, {RowOffset, ColumnOffset}}, _From, State) ->
	{Row, Column} = State#state.cursor_pos,
	{MaxRows, MaxColumns} = context:get_window_size(State#state.context),
	ActualRow = utils:max(1, utils:min(MaxRows, Row + RowOffset)),
	ActualColumn = utils:max(1, utils:min(MaxColumns, Column + ColumnOffset)),
	{reply, ok, State#state{cursor_pos = {ActualRow, ActualColumn}}};

handle_call(new_line, _From, State) ->
	{Row, _} = State#state.cursor_pos,
	{MaxRows, _} = context:get_window_size(State#state.context),
	ActualRow = utils:max(1, utils:min(MaxRows, Row + 1)),
	{reply, ok, State#state{cursor_pos = {ActualRow, 1}}};

handle_call(_Other, _From, State) ->
	{reply, undef, State}.

%% --------------------------------------------------------------------
%% Function: handle_cast/2
%% Description: Handling cast messages
%% Returns: {noreply, State}          |
%%          {noreply, State, Timeout} |
%%          {stop, Reason, State}            (terminate/2 is called)
%% --------------------------------------------------------------------
handle_cast(stop, State) ->
    {stop, normal, State}.

%% --------------------------------------------------------------------
%% Function: handle_info/2
%% Description: Handling all non call/cast messages
%% Returns: {noreply, State}          |
%%          {noreply, State, Timeout} |
%%          {stop, Reason, State}            (terminate/2 is called)
%% --------------------------------------------------------------------
handle_info(_Info, State) ->
    {noreply, State}.

%% --------------------------------------------------------------------
%% Function: terminate/2
%% Description: Shutdown the server
%% Returns: any (ignored by gen_server)
%% --------------------------------------------------------------------
terminate(_Reason, _State) ->
    ok.

%% --------------------------------------------------------------------
%% Func: code_change/3
%% Purpose: Convert process state when code is changed
%% Returns: {ok, NewState}
%% --------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

%% --------------------------------------------------------------------
%%% Internal functions
%% --------------------------------------------------------------------

